from xml.etree.ElementTree import XMLParser

def sacar_comillas_de_tipo_datos(c):
    for clase in c.clases.keys():
        for atributo in c.clases[clase].atributos.keys():
            aux = c.clases[clase].atributos[atributo].tipo
            c.clases[clase].atributos[atributo].tipo = aux.strip("'")
        for metodo in c.clases[clase].metodos.keys():
            for parametro in c.clases[clase].metodos[metodo].parametros.keys():

                if c.clases[clase].metodos[metodo].parametros[parametro].tipo=={}:
                    aux=''
                else:
                    aux=c.clases[clase].metodos[metodo].parametros[parametro].tipo
                    aux=aux.strip("'")

                c.clases[clase].metodos[metodo].parametros[parametro].tipo = aux
    return c

class clase:
    def __init__(self):
        self.propiedades={}
        self.atributos={}
        self.metodos={}
        self.padre=''
        self.depth=5

class metodo:
    def __init__(self):
        self.propiedades={}
        self.parametros={}
        self.depth=0
class atributo:
    def __init__(self):
        self.propiedades={}
        self.tipo={}
        self.depth=0

class parametro:
    def __init__(self):
        self.propiedades={}
        self.tipo={}
        self.depth=0

class asociacion:
    def __init__(self):
        self.propiedades={}
        self.clase1=''
        self.propiedades1={}
        self.clase2=''
        self.propiedades2={}
        self.multiplicity=''
        self.participante=1


class MiParser(object):
    def __init__(self):
        self.c=clase()  #objeto del tipo clase
        self.m=metodo()
        self.p=parametro()
        self.a=atributo()
        self.clases ={} #Diccionario de clases, voy guardando todas las clses que aparecen
        self.modo=''#indica si estoy en modo atributo o parametro, para ver a quien pertenece le tipo
        #Contol de profundidad
        self.maxDepth = 0
        self.depth = 0
        self.depth_tipos=0

        self.dic_herencia={} #Diccionario con los id herencia de cada clase
        self.nombre_hijo=''
        self.padre=''

        self.ocl=[]
        self.r=asociacion()
        self.relaciones={}
        self.cant_relaciones=0

        #------------------DICCIONARIO DE TIPOS argoUML----------------------------#
        self.dic_tipos={'http://argouml.org/profiles/uml14/default-uml14.xmi#-84-17--56-5-43645a83:11466542d86:-8000:000000000000087C':"'Integer'",
               'http://argouml.org/profiles/uml14/default-uml14.xmi#-84-17--56-5-43645a83:11466542d86:-8000:000000000000087D':"'UnlimitedInteger'",
               'http://argouml.org/profiles/uml14/default-uml14.xmi#-84-17--56-5-43645a83:11466542d86:-8000:000000000000087E':"'String'",
               'http://argouml.org/profiles/uml14/default-uml14.xmi#-84-17--56-5-43645a83:11466542d86:-8000:0000000000000880':"'Boolean'",
               '.:000000000000086B':"'void'",
               '.:000000000000086C':"'int'",
               '.:000000000000086D':"'short'",
               '.:000000000000086E':"'long'",
               '.:000000000000086F':"'double'",
               '.:0000000000000870':"'float'",
               '.:0000000000000871':"'char'",
               '.:0000000000000872':"'byte'",
               '.:0000000000000873':"'boolean'"}
        #--------------------------------------------------------------------------#

    def start(self, tag, attrib): # Llamada por cada tag abierto.

        #--------Profundidad-----------#
        self.depth += 1
        if self.depth > self.maxDepth:
            self.maxDepth = self.depth
        #------------------------------#

        if tag.endswith('Class'):
            if self.depth<=self.c.depth:
                self.c.propiedades=attrib
                self.c.depth=self.depth
                self.dic_tipos.update({attrib['xmi.id']:attrib['name']})


            #----------------Clases que derivan de Relaciones----------------#
            if tag.endswith('AssociationClass'):
                self.r.propiedades=attrib

            if tag.endswith('AssociationEnd'):
                if self.r.participante==1:
                    self.r.propiedades1=attrib
                else:
                    self.r.propiedades2=attrib

            if tag.endswith('AssociationEnd.participant'):
                self.modo='r'
            #------------------------------------------#





            #-------------Tipos clase----------------------------#
            if self.depth>self.depth_tipos:
                if self.modo=='p':
                    if self.depth>self.p.depth:
                        if attrib['xmi.idref'] in self.dic_tipos:
                            self.p.tipo=self.dic_tipos[attrib['xmi.idref']]
                        else:
                            self.p.tipo= attrib['xmi.idref']
                if self.modo=='a':
                    if self.depth>self.a.depth:
                        if attrib['xmi.idref'] in self.dic_tipos:
                            self.a.tipo=self.dic_tipos[attrib['xmi.idref']]
                        else:
                            self.a.tipo= attrib['xmi.idref']

            if self.padre<>'':
                self.padre=attrib['xmi.idref']
                self.clases[self.nombre_hijo].padre= self.dic_tipos[self.padre]
                self.padre=''

            if self.modo=='r':
                if self.r.participante==1:
                    self.r.clase1= self.dic_tipos[attrib['xmi.idref']]
                else:
                    self.r.clase2= self.dic_tipos[attrib['xmi.idref']]
                self.modo=''


        if tag.endswith('Attribute'):
            self.a.depth=self.depth
            self.a.propiedades=attrib
            self.modo='a'

        if tag.endswith('Operation'):
            self.m.depth=self.depth
            self.m.propiedades=attrib

        if tag.endswith('Parameter'):
            self.p.depth=self.depth
            self.p.propiedades=attrib
            self.modo='p'




        if tag.endswith('DataType') or tag.endswith('Enumeration'):
            if self.modo=='p':
                if self.depth>self.p.depth:
                    if attrib['href'] in self.dic_tipos:
                        self.p.tipo=self.dic_tipos[attrib['href']]

            if self.modo=='a':
                if self.depth>self.a.depth:
                    if attrib['href'] in self.dic_tipos:
                        self.a.tipo=self.dic_tipos[attrib['href']]

        if tag.endswith('StructuralFeature.type'):
            self.depth_tipos=self.depth



        #-----------Control de clases--------------#
        if tag.endswith('Generalization'):
            if self.depth>self.c.depth:
                self.dic_herencia.update({attrib['xmi.idref']:self.c.propiedades['name']})

            else:
                self.nombre_hijo=self.dic_herencia[attrib['xmi.id']]

        if tag.endswith('Generalization.parent'):
            self.padre='a'
        #------------------------------------------#

        #----------------OCL-----------------------#
        if tag.endswith('BooleanExpression'):
            self.ocl.append(attrib['body']+'\n')

        #------------------------------------------#

        #----------------Relaciones----------------#
        if tag.endswith('Association'):
            self.r.propiedades=attrib


        if tag.endswith('AssociationEnd'):
            if self.r.participante==1:
                self.r.propiedades1=attrib
            else:
                self.r.propiedades2=attrib

        if tag.endswith('AssociationEnd.participant'):
            self.modo='r'
        #------------------------------------------#



    def end(self, tag):             # Llamada por cada tag cerrado.

        if tag.endswith('Class'):
            if self.depth<=self.c.depth:
                self.nueva_clase()

            #----------------Clases que derivan de Relaciones----------------#
            if tag.endswith('AssociationClass'):
                self.nueva_relacion()

        if tag.endswith('Attribute'):
            if self.depth<=self.a.depth:
                self.nuevo_atributo()
                self.modo=''

        if tag.endswith('Operation'):
            if self.depth<=self.m.depth:
                self.nuevo_metodo()

        if tag.endswith('Parameter'):
            if self.depth<=self.p.depth:
                self.nuevo_parametro()
                self.modo=''

        if tag.endswith('XMI'):
        #Para los atributos o parametros que tienen como tipo una clase
            for i in self.clases.values():
                for j in i.atributos.values():
                    if j.tipo in self.dic_tipos.keys():
                        j.tipo=self.dic_tipos[j.tipo]
                for j in i.metodos.values():
                    for h in j.parametros.values():
                        if h.tipo in self.dic_tipos.keys():
                            h.tipo=self.dic_tipos[h.tipo]


        #----------------Relaciones----------------#
        if tag.endswith('AssociationEnd.participant'):
            if self.r.participante==1:
                self.r.participante=2
            else:
                self.r.participante=1

        if tag.endswith('Association'):
            self.nueva_relacion()
        #------------------------------------------#






        #--------Profundidad-----------#
        self.depth -= 1
        #------------------------------#



    def data(self, data):
        pass            # No necesitamos hacer nada con data.

    def close(self):    # Llamada cuando todos los datos han sido parseados.
        pass

    def nueva_clase(self):
        if 'name' in self.c.propiedades:
            self.clases.update({self.c.propiedades['name']:self.c})
        self.c=clase()


    def nuevo_atributo(self):
        self.a.propiedades['collection']='false'
        if 'name' in self.a.propiedades:
            self.c.atributos.update({self.a.propiedades['name']:self.a})
        self.a=atributo()


    def nuevo_metodo(self):
        if 'name' in self.m.propiedades:
            self.c.metodos.update({self.m.propiedades['name']:self.m})
        self.m=metodo()


    def nuevo_parametro(self):
        if 'name' in self.p.propiedades:
            self.m.parametros.update({self.p.propiedades['name']:self.p})
        self.p=parametro()


    def nueva_relacion(self):
        if 'name' in self.r.propiedades:
            self.cant_relaciones= self.cant_relaciones+1
            if self.r.propiedades['name']=='':
                self.relaciones.update({self.cant_relaciones:self.r})
            else:
                self.relaciones.update({self.r.propiedades['name']:self.r})
        self.r=asociacion()




def intermedio(archivo,parser):

    for clase in parser.clases:

        archivo.write('\n<class ')

        for prop in parser.clases[clase].propiedades:

            if prop=='name' or prop=='visibility':

                archivo.write(prop+' = '+ parser.clases[clase].propiedades[prop]+' ')

        archivo.write('padre = ' + parser.clases[clase].padre +'>\n')


        #Atributos#
        for atrib in parser.clases[clase].atributos:

            archivo.write(tabs(1)+'<atrib ')

            for prop in parser.clases[clase].atributos[atrib].propiedades:

                if prop=='name' or prop=='visibility' or prop=='collection':

                    archivo.write(prop+' = '+ parser.clases[clase].atributos[atrib].propiedades[prop]+' ')

            archivo.write('tipo = '+ parser.clases[clase].atributos[atrib].tipo+'/>\n')


        #Metodos#
        for metodo in parser.clases[clase].metodos:

            archivo.write('\n'+tabs(1)+ '<metodo ')

            for prop in parser.clases[clase].metodos[metodo].propiedades:

                if prop=='name' or prop=='visibility':

                    archivo.write(prop+' = '+ parser.clases[clase].metodos[metodo].propiedades[prop]+' ')

            archivo.write('>\n')


            #Parametros#
            for param in parser.clases[clase].metodos[metodo].parametros:

                archivo.write(tabs(2)+ '<parametro ')

                for prop in parser.clases[clase].metodos[metodo].parametros[param].propiedades:

                    if prop=='name' or prop=='visibility':

                        archivo.write(prop+' = '+ parser.clases[clase].metodos[metodo].parametros[param].propiedades[prop]+' ')

                archivo.write('tipo = '+ parser.clases[clase].metodos[metodo].parametros[param].tipo+'/>\n')            

            archivo.write(tabs(1)+ '</metodo>')

        archivo.write('\n</class>')

        
def tabs(n):
    t=''
    while n>0:
        t=t+'\t'
        n=n-1
    return t

def agregar_atributo(clase,nombre,tipo,clases):
    #agrega un atributo coleccion
    a=atributo()

    a.propiedades['name']=nombre
    a.propiedades['visibility']='public'
    a.propiedades['collection']='true'
    a.tipo=tipo

    clases[clase].atributos[nombre]=a

def agregar_colecciones(a):
    for i in a.relaciones:
        clase1=a.relaciones[i].clase1
        clase2=a.relaciones[i].clase2
        
        if es_clase(i,a.clases.keys()):
            #agrego dos atributos en cada participante y en la clase derivada de la relacion
            
            #clase 1
            nombre=clase2.lower()+'s'
            agregar_atributo(clase1,nombre,clase2,a.clases)
            nombre=i.lower()+'s'
            agregar_atributo(clase1,nombre,i,a.clases)

            #clase 2
            nombre=clase1.lower()+'s'
            agregar_atributo(clase2,nombre,clase1,a.clases)
            nombre=i.lower()+'s'
            agregar_atributo(clase2,nombre,i,a.clases)

            #clase 3 (relacion)
            nombre=clase1.lower()+'s'
            agregar_atributo(i,nombre,clase1,a.clases)
            nombre=clase2.lower()+'s'
            agregar_atributo(i,nombre,clase2,a.clases)
        else:
            #agrego un atributo en el participante que tiene aggregation='aggregate'
            if a.relaciones[i].propiedades1['aggregation']=='aggregate':
                nombre=clase2.lower()+'s'
                agregar_atributo(clase1,nombre,clase2,a.clases)
                
            if a.relaciones[i].propiedades2['aggregation']=='aggregate':
                nombre=clase1.lower()+'s'
                agregar_atributo(clase2,nombre,clase1,a.clases)


def ejecutar():
    t = MiParser()
    parser = XMLParser(target=t)
    parser.feed(''.join(open('xml/prueba11.xmi','r').readlines()))
    parser.close()

    #---Guarda OCL---#
    archivo=open('ocl.txt','w')
    for i in t.ocl:

        #Separa tokens
        i=i.replace(':',' : ')
        i=i.replace(' :  : ',' :: ')
        i=i.replace('<',' < ')
        i=i.replace('>',' > ')
        i=i.replace('-',' - ')
        i=i.replace(' -  > ',' -> ')
        i=i.replace('(',' ( ')
        i=i.replace(')',' ) ')
        i=i.replace('=',' = ')
        i=i.replace('.',' . ')
        i=i.replace('+',' + ')
        i=i.replace('*',' * ')
        i=i.replace('/',' / ')
        i=i.replace('|',' | ')
        i=i.replace(',',' , ')
        i=i.replace('{',' { ')
        i=i.replace('}',' } ')
        i=i.replace('[',' [ ')
        i=i.replace(']',' ] ')
        i=i.replace(';',' ; ')
        i=i.replace(' >  = ',' >= ')
        i=i.replace(' <  = ',' <= ')
        i=i.replace(' <  > ',' <> ')
        i=i.replace(' .  . ',' .. ')
        i=i.replace('@',' @ ')
            

        archivo.write(i)


    archivo.close()
    #----------------#

    
    t = sacar_comillas_de_tipo_datos(t)

    agregar_colecciones(t)

    #---Guarda intermedio---#
    archivo=open('intermedio_uml.xmi','w')
    intermedio(archivo,t)
    archivo.close()
    #-----------------------#

    
    return t

def es_clase(rel,clases):
    for i in clases:
        if rel == i:
            return True
    return False

a= ejecutar()


        
            
            
